package com.hzw.saas.service.project.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.MybatisPlusUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.flow.pojo.enums.FlowStatus;
import com.hzw.saas.common.flow.pojo.model.FlowInstance;
import com.hzw.saas.common.flow.service.IFlowInstanceService;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.enums.AssertEnum;
import com.hzw.saas.common.util.exception.ForbiddenException;
import com.hzw.saas.service.project.bo.ProjectApplyBO;
import com.hzw.saas.service.project.bo.ProjectApplyInfoBO;
import com.hzw.saas.service.project.bo.ProjectApplyScrollBo;
import com.hzw.saas.service.project.constants.ProjectConsts;
import com.hzw.saas.service.project.mapper.ProjectApplyMapper;
import com.hzw.saas.service.project.model.ProjectApply;
import com.hzw.saas.service.project.service.IProjectApplyService;
import com.hzw.saas.service.project.service.IProjectAttachmentService;
import com.hzw.saas.service.project.util.PdfUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author sonam
 * @since 2021-08-04
 */
@Service
@RequiredArgsConstructor
public class ProjectApplyServiceImpl extends ServiceImpl<ProjectApplyMapper, ProjectApply> implements IProjectApplyService {

    @Resource
    private IFlowInstanceService flowInstanceService;

    @Resource
    private IProjectAttachmentService projectAttachmentService;

    // @Resource
    // private IResourceFileService resourceFileService;

    @Override
    public String getProjectApplyId(String flowInstanceId) {
        LambdaQueryChainWrapper<ProjectApply> query = super.lambdaQuery()
            .select(ProjectApply::getPid)
            .eq(ProjectApply::getFlowInstanceId, flowInstanceId);
        MybatisPlusUtil.limitOne(query);
        ProjectApply one = query.one();
        return one == null ? null : one.getPid();
    }

    @Override
    public ProjectApplyBO getProjectApply(String flowInstanceId, String projectApplyId) {
        ProjectApply projectApplyDO = this.getProjectApplyDO(flowInstanceId, projectApplyId);
        return MapperUtil.nf().map(projectApplyDO, ProjectApplyBO.class);
    }

    @Override
    public IPage<ProjectApplyInfoBO> pageProjectApply(PageParam pageParam, String flowId, String fuzzy, Integer type, List<String> statusIds, Integer notifyState, List<String> levelIds, Integer sort) {
        return this.baseMapper.pageProjectApply(pageParam.convert(), flowId, fuzzy, type, statusIds, notifyState, levelIds, sort);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProjectApply createProjectApply(ProjectApply query, boolean isSubmit) {
        // 查看是否已存在流程
        Integer count = flowInstanceService.lambdaQuery()
            .eq(FlowInstance::getFlowId, ProjectConsts.PROJECT_FLOW_ID)
            .eq(FlowInstance::getSubmitUserId, query.getUserId())
            .ne(FlowInstance::getState, FlowStatus.COMPLETE.code())
            .ne(FlowInstance::getState, FlowStatus.ERROR.code())
            .count();
        AssertUtil.assertThrow("已存在进行中的项目审批流程", HttpStatus.BAD_REQUEST, count > 0);
        FlowInstance flowInstance = flowInstanceService.createFlowInstance(query.getUserId(), ProjectConsts.PROJECT_FLOW_ID, query.getProjectName());
        query.setPid(SnowFlakeUtil.nextIdStr());
        query.setFlowInstanceId(flowInstance.getPid());
        super.save(query);
        if (isSubmit) {
            flowInstanceService.doFlowProcess(flowInstance.getPid(), ProjectConsts.FLOW_PROCESS_SUBMIT_ID, query.getUserId(), "");
        }
        return query;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProjectApply(ProjectApply query, boolean isSubmit) {
        AssertUtil.assertThrow("流程实例ID和项目申请ID不可同时为空", StrUtil.isAllBlank(query.getPid(), query.getFlowInstanceId()));
        ProjectApply originApply = this.getProjectApplyDO(query.getFlowInstanceId(), query.getPid());
        AssertUtil.assertThrow("无法找到项目申请", Objects.isNull(originApply));
        AssertUtil.assertThrow("无权限", !Objects.equals(originApply.getUserId(), query.getUserId()));
        FlowInstance flowInstance = flowInstanceService.getById(originApply.getFlowInstanceId());
        AssertUtil.assertThrow("申请流程无法找到", Objects.isNull(flowInstance));
        // 检查当前流程节点是否可以更新
        boolean editable = ProjectConsts.FLOW_NODE_EDITABLE.containsKey(flowInstance.getFlowNodeId());
        AssertUtil.assertThrow("当前流程节点不可编辑", !editable);
        query.setPid(originApply.getPid());
        super.updateById(query);

        if (StrUtil.isNotBlank(query.getProjectName())) {
            flowInstanceService.lambdaUpdate()
                .set(FlowInstance::getFlowInstanceName, query.getProjectName())
                .eq(FlowInstance::getPid, originApply.getFlowInstanceId())
                .update();
        }
        if (isSubmit) {
            flowInstanceService.doFlowProcess(flowInstance.getPid(),
                ProjectConsts.FLOW_NODE_EDITABLE.get(flowInstance.getFlowNodeId()), query.getUserId(), "");
        }
    }

    @Override
    public void projectApplyExportPDF(String flowInstanceId, HttpServletResponse response, boolean isAdmin) {
        FlowInstance flowInstance = flowInstanceService.getById(flowInstanceId);
        AssertUtil.assertThrow("项目申请未审批", Objects.isNull(flowInstance));
        String flowNodeId = flowInstance.getFlowNodeId();
        boolean canExport = Objects.equals(flowNodeId, ProjectConsts.PROJECT_PASS_NODE_ID) ||
            ObjectUtil.compare(flowNodeId, ProjectConsts.PROJECT_WAITING_ACCEPTANCE_ID) >= 0;

        AssertUtil.assertThrow("项目申报未通过审批，不能导出PDF", !isAdmin && !canExport);

        List<ProjectApply> projectApplies = this.lambdaQuery().eq(ProjectApply::getFlowInstanceId, flowInstanceId).list();
        AssertUtil.assertThrow("项目申请信息为空", CollectionUtil.isEmpty(projectApplies));
        ProjectApply projectApply = projectApplies.get(0);

        try {
            // 获取logo
            byte[] logBytes = HttpUtil.downloadBytes(ProjectConsts.PROJECT_APPLY_PDF_LOGO_S3_URL);
            // 获取水印
            byte[] remarkBytes = HttpUtil.downloadBytes(ProjectConsts.PROJECT_APPLY_PDF_REMARK_S3_URL);
            // 水印输入
            ByteArrayInputStream markInput = new ByteArrayInputStream(remarkBytes);
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/pdf");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(projectApply.getProjectName() + ".pdf", "UTF-8"));
            PdfUtils.createProjectApplyPDF(response.getOutputStream(), new ByteArrayInputStream(logBytes),
                isAdmin ? null : markInput,
                isAdmin ? "鸿之微横向项目申请表" : null,
                projectApply);
        } catch (Exception e) {
            e.printStackTrace();
            throw DefaultSaasException.build("项目申请导出，异常请联系管理员", e);
        }
    }

    @Override
    public void updateProjectAcceptance(String projectApplyId, String userId, String acceptanceInstruction, boolean isSubmit) {
        ProjectApply projectApply = this.getProjectApplyDO(null, projectApplyId);

        AssertUtil.assertExistence(AssertEnum.FOUND, projectApply, "项目申请");
        Assert.isTrue(Objects.equals(projectApply.getUserId(), userId), () -> new ForbiddenException("无权限"));

        FlowInstance flowInstance = flowInstanceService.getById(projectApply.getFlowInstanceId());
        AssertUtil.assertExistence(AssertEnum.FOUND, flowInstance, "项目申请流程");

        // 检查当前流程节点是否可以更新
        boolean editable = ProjectConsts.FLOW_NODE_EDITABLE.containsKey(flowInstance.getFlowNodeId());
        AssertUtil.assertBoolean(AssertEnum.TRUE, editable, "当前节点可编辑性：");

        ProjectApply entity2Update = new ProjectApply();
        entity2Update.setPid(projectApplyId);
        entity2Update.setAcceptanceInstruction(acceptanceInstruction);
        super.updateById(entity2Update);

        if (isSubmit) {
            flowInstanceService.doFlowProcess(flowInstance.getPid(),
                ProjectConsts.FLOW_NODE_EDITABLE.get(flowInstance.getFlowNodeId()), userId, "");
        }
    }

    private ProjectApply getProjectApplyDO(String flowInstanceId, String projectApplyId) {
        if (StrUtil.isAllBlank(flowInstanceId, projectApplyId)) {
            return null;
        }
        return super.lambdaQuery()
            .eq(StrUtil.isNotBlank(projectApplyId), ProjectApply::getPid, projectApplyId)
            .or()
            .eq(StrUtil.isNotBlank(flowInstanceId), ProjectApply::getFlowInstanceId, flowInstanceId)
            .last("LIMIT 1").one();
    }

    @Override
    public List<ProjectApplyScrollBo> limitProjectScroll(Integer count) {
        AssertUtil.assertThrow("数量需大于零", count < 1);
        return this.baseMapper.limitProjectScroll(count);
    }
}
